---@class Battle.Bullet
local Bullet = Battle.CreateClass("Bullet")
Battle.Bullet = Bullet
function Bullet:OnCreate()
    self.id = 0
    self.speed = 15
    self.curTime = 0
    self.curMoveTime = 0
    self.moveTime = 0
    self.targetPos = self.targetPos or Vector3.New()
    ---@type Battle.Skill
    self.skill = nil
    self.targetId = 0   --目标对象
    self.during = 5
    self.isDie = false
    self.startPos = self.startPos or Vector3.New()
    self.endPos = self.endPos or Vector3.New()
    self.pos = self.pos or Vector3.New()
    self.forward = self.forward or Vector3.New()
    self.ctrlPoint = self.ctrlPoint or Vector3.New()

    self.targetPos:Set(0,0,0)
    self.forward:Set(0,0,0)
end
function Bullet:Reset()
    self:OnCreate()
end
function Bullet:SetDamage(damage,hurtType)
    self.damage = damage
    self.hurtType = hurtType
end
function Bullet:SetTransfer(pos,forward)
    self.pos:SetV(pos)
    self.forward:SetV(forward)
end
function Bullet:Execute()
    self.curTime = self.during
    self:InitMove()
    Battle.GlobalEventSystem.BroadCast(Battle.Event.BulletCreate,Battle.EventArgs.BulletArgs:Create(self))
end
function Bullet:SetBullet(uniqueId,bulletId,skill,targetId)
    self.id = uniqueId
    self.bulletId = bulletId
    local cfg = DataTable.GetById("t_bullet",bulletId)
    if not cfg then
        logError("找不到t_bullet "..bulletId)
        return
    end
    self.skill = skill
    self.actor = self.skill.actor
    self.targetId = targetId or 0
    self.isLock = cfg.f_Locking == 1 --是否锁定目标
    self.speed = cfg.f_Speed/1000*10
    self.targetPos:SetV(self.skill.targetHitPos)
    ---伤害方式
    local hurtMode = decodeJsonStrToTable(cfg.f_HurtMode)
    self.hitType = hurtMode[1]
    self.flyMaxDistance =  hurtMode[2]
    ---飞行路径
    local flyMode = decodeJsonStrToTable(cfg.f_FlightRoute)
    self.flyType = flyMode[1]
    self.gravity = flyMode[2]
    ---伤害范围
    local range = decodeJsonStrToTable(cfg.f_HurtRange)
    self.hurtRangeType = range[1]
    self.hurtRangeRadius = range[2]
    ---伤害是否可以重复
    self.isDamageCanRepeat = cfg.f_HurtOnly
    self.moveTotalDis = 0
    self.moveTime = 0
    self.buffParamList = decodeJsonStrToTable(cfg.f_BulletBuff)
    self.damageList = {}
end
function Bullet:OnDestroy()
end
function Bullet:CheckMoveChanged()
    if not self:IsTargetMove() then
        return
    end
    self:InitMove()
end
function Bullet:InitMove()
    self.startPos:SetV(self.pos)
    self.endPos:SetV(self.targetPos)

    self.curMoveTime = 0
    if self.flyType == Battle.BulletFlyType.Curve then
        self.startPos.y = 0
        self.endPos.y = 0
        self.moveTime = Vector3.Distance(self.startPos,self.endPos)/self.speed + 0.0001
        local h = self.targetPos.y-self.pos.y
        self.upVo = (h+0.5*self.gravity*self.moveTime*self.moveTime)/self.moveTime
    else
        self.moveTime = Vector3.Distance(self.startPos,self.endPos)/self.speed
    end
end
function Bullet:Update(dt)
    if self.isDie then
        return
    end
    self:CheckMoveChanged()
    self.curMoveTime = self.curMoveTime + dt
    local t = self.curMoveTime/self.moveTime

    if self.flyType == Battle.BulletFlyType.Curve then
        self.pos.x = (1-t)*self.startPos.x + t*self.endPos.x
        self.pos.z = (1-t)*self.startPos.z + t*self.endPos.z
        self.upVo = self.upVo - self.gravity*dt
        self.pos.y = self.pos.y + self.upVo*dt
    else
        Vector3.Lerp2(self.startPos,self.endPos,t,self.pos)
    end

    Battle.GlobalEventSystem.BroadCast(Battle.Event.BulletPosChanged,Battle.EventArgs.BulletArgs:Create(self))

    local list = Battle.ActorManager.GetActorList()
    local target = self:GetTarget()
    for k,v in pairs(list) do
        if v.camp ~= self.actor.camp and not self.damageList[v.id] then
            local isHit = self:IsHit(v)
            if isHit then
                if self.hitType == Battle.BulletHitType.FirstTarget then
                    self:Hit(v)
                    self:Die()
                    return
                elseif self.hitType == Battle.BulletHitType.Target and v ==target then
                    self:Hit(v)
                    self:Die()
                    return
                elseif self.hitType == Battle.BulletHitType.Path then
                    self:Hit(v)
                end
            end
        end
    end
    if self.curMoveTime >= self.moveTime then
        self:Die()
    end
    self.curTime = self.curTime - dt
    if self.curTime <= 0 then
        self:Die()
    end
end
function Bullet:GetBezier(start,_end,ctrl,t)
    if t > 1 then
        t = 1
    end
    return (1-t)^2*start + 2*t*(1-t)*ctrl + t^2* _end
end
function Bullet:IsDie()
    return self.isDie
end
function Bullet:Die()
    if self.isDie then return end
    self.isDie = true
    Battle.GlobalEventSystem.BroadCast(Battle.Event.BulletDie,Battle.EventArgs.BulletArgs:Create(self))
end
---@param actor Battle.Entity
function Bullet:IsHit(actor)
    local x = self.pos.x - actor.pos.x
    local z = self.pos.z - actor.pos.z
    return x*x+z*z < actor.radius*actor.radius
end
---@return Battle.Entity
function Bullet:GetTarget()
    return Battle.ActorManager.GetActor(self.targetId)
end
function Bullet:IsTargetMove()
    if not self.isLock then
        return false
    end
    local target = self:GetTarget()
    if not target then return false end
    if math.abs(self.targetPos.x - target.pos.x) < 0.1 and math.abs(self.targetPos.z - target.pos.z ) < 0.1 then
        return false
    end
    self.targetPos:SetV(target:GetHitPos())
    return true
end
---@param actor Battle.Entity
function Bullet:Hit(actor)
    local RangeType = Battle.BulletHurtRangeType
    local resHit = DataTable.GetById("t_skillshow",self.skill.skillId,"f_HitSE")
    if self.hurtRangeType == RangeType.Single then
        if not actor then return end
        if not self.actor:CanTarget(actor) then return end
        self.damageList[actor.id] = true
        local damageData = Battle.BattleUtil.CalcRealDamage(self.damage,self.hurtType,self.actor,actor,Battle.SourceType.Bullet,self.bulletId)
        self.actor:Damage(damageData)
        self:AddBuff(actor)
        Battle.EffectShowMgr.PlayEffect(resHit,actor:GetHitPos(),self.forward,0.5,self.id)
        return
    end
    if self.hurtRangeType == RangeType.Circle then
        Battle.EffectShowMgr.PlayEffect(resHit,self.pos,self.forward,0.5,self.id)
        local list = Battle.ActorManager.GetActorList()
        for k,v in pairs(list) do
            if v.camp ~= self.actor.camp and not self.damageList[v.id] and self.actor:CanTarget(v) then
                local isHit = Battle.BattleUtil.IsCircleIntersection(self.pos,self.hurtRangeRadius,v.pos,v.radius)
                if isHit then
                    self.damageList[v.id] = true
                    local damageData = Battle.BattleUtil.CalcRealDamage(self.damage,self.hurtType,self.actor,v,Battle.SourceType.Bullet,self.bulletId)
                    self.actor:Damage(damageData)
                    self:AddBuff(v)
                end
            end
        end
    end
end
---@param actor Battle.Entity
function Bullet:AddBuff(actor)
    if not self.buffParamList then return end
    for k,v in pairs(self.buffParamList) do
        local type = v[1]
        local id = v[2]
        local layer = v[3]
        if type == 1 then--命中
            actor:AddBuff(id,layer,nil,self.actor,self.skill.skillId)
        end
    end
end